﻿namespace Resharper.Abstractions.Shell
{
  using System;
  using JetBrains.ActionManagement;
  using JetBrains.Annotations;
  using JetBrains.Application;
  using JetBrains.Application.CommandProcessing;
  using JetBrains.Application.Progress;
  using JetBrains.DataFlow;
  using JetBrains.DocumentModel;
  using JetBrains.ProjectModel.DataContext;
  using JetBrains.ReSharper.Psi;
  using JetBrains.Threading;
  using JetBrains.UI.Application.Progress;
  using JetBrains.Util;

  /// <summary>Defines the <see cref="ShellManager"/> class.</summary>
  [ShellComponent]
  public class ShellManager
  {
    #region Public Methods

    /// <summary>Gets the component.</summary>
    /// <typeparam name="T"></typeparam>
    /// <returns>Returns the component.</returns>
    public static T GetComponent<T>() where T : class
    {
      return Shell.Instance.GetComponent<T>();
    }

    /// <summary>Executes the cookie.</summary>
    /// <param name="context"></param>
    /// <param name="key">The key.</param>
    /// <param name="cookieName">Name of the cookie.</param>
    /// <param name="action">The action.</param>
    public static void ExecuteCookie(DataContext context, string key, string cookieName, Action action)
    {
      /*
      Shell.Instance.GetComponent<IThreading>().ReentrancyGuard.ExecuteOrQueue(
        key,
        delegate
        {
          using (ReadLockCookie.Create())
          {
            using (Shell.Instance.GetComponent<ICommandProcessor>().UsingCommand(cookieName))
            {
              action();
            }
          }
        });
      */

      var solution = context.InternalDataContext.GetData(DataConstants.SOLUTION);
      if (solution == null)
      {
        throw new InvalidOperationException("solution is null");
      }

      Action inner = delegate
      {
        using (ReadLockCookie.Create())
        {
          var psiServices = solution.GetPsiServices();

          using (Shell.Instance.GetComponent<ICommandProcessor>().UsingCommand(cookieName))
          {
            using (new OpenDocumentAfterModificationCookie(Shell.Instance.GetComponent<DocumentSettings>()))
            {
              var cookie = solution.CreateTransactionCookie(DefaultAction.Rollback, cookieName);
              try
              {
                Shell.Instance.GetComponent<UITaskExecutor>().SingleThreaded.ExecuteTask(cookieName, TaskCancelable.Yes, (progress =>
                {
                  progress.TaskName = cookieName;

                  var transactionResult = psiServices.PsiManager.DoTransaction(action, cookieName);
                  if (transactionResult.Succeded)
                  {
                    cookie.Commit();
                  }
                }));
              }
              finally
              {
                if (cookie != null)
                {
                  cookie.Dispose();
                }
              }
            }
          }
        }
      };

      ExecuteReentrancyGuard(key, inner);
    }

    /// <summary>Executes the reentrancy guard.</summary>
    /// <param name="key">The key.</param>
    /// <param name="action">The action.</param>
    public static void ExecuteReentrancyGuard([NotNull] string key, [NotNull] Action action)
    {
      if (key == null)
      {
        throw new ArgumentNullException("key");
      }

      if (action == null)
      {
        throw new ArgumentNullException("action");
      }

      Shell.Instance.GetComponent<IThreading>().ReentrancyGuard.ExecuteOrQueue(key, action);
    }

    /// <summary>Inits this instance.</summary>
    public void Init()
    {
      var actionManager = Shell.Instance.GetComponent<ActionManager>();

      var action = actionManager.TryGetAction("CompleteStatement") as IUpdatableAction;
      if (action != null)
      {
        action.AddHandler(EternalLifetime.Instance, new CompleteStatementContextAction());
      }
    }

    #endregion
  }
}